﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using DisplayBoardInfo.Network.Messages;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace DisplayBoardInfo.Network
{
    public class Client
    {
        TcpClient client;
        private int bufferSize = short.MaxValue;

        public delegate void MessageRecivedHandler(object sender, MessageEventArgs args);
        public event MessageRecivedHandler MessageRecived;

        public Client()
        {
            this.MessageRecived += new MessageRecivedHandler(Client_MessageRecived);
        }

        /// <summary>
        /// Connects to specific server.
        /// </summary>
        /// <param name="ipAddress">Server's IP address.</param>
        public void Connect(string ipAddress)
        {
            try
            {
                client = new TcpClient();
                while (!client.Connected)
                {
                    client.Connect(IPAddress.Parse(ipAddress), 9000);
                }
                Recive();
            }
            catch (SocketException ex)
            {
                throw new ClientException(ex.Message);
            }
        }

        /// <summary>
        /// Sends message to sever.
        /// </summary>
        /// <param name="message">Message to send.</param>
        public void Send(NetworkMessage message)
        {
            if (client.Connected)
            {
                NetworkStream stream = client.GetStream();

                MemoryStream memStream = new MemoryStream();

                BinaryFormatter bf = new BinaryFormatter();
                bf.Serialize(memStream, message);

                byte[] byteMessage = memStream.GetBuffer();

                //if (byteMessage.Length > 256) //if message is bigger than 2000 then we have to increase recive buffer
                //{
                //    Network.Messages.NetworkMessage sMessage = new NetworkMessage(NetworkMessageTypes.Text, "#s#" + byteMessage.Length.ToString());

                //    Send(sMessage);
                //}

                stream.Write(byteMessage, 0, byteMessage.Length);
                stream.Flush();
            }
            else throw new ClientException("Client is not connect.");
        }

        /// <summary>
        /// Close current TCP connection.
        /// </summary>
        public void Close()
        {
            if (client.Connected) client.Close();
            client = null;
        }

        private void Recive()
        {
            new Thread(
                delegate()
                {
                    NetworkStream stream = client.GetStream();

                    Byte[] bytes = new Byte[bufferSize];

                    while (client.Connected)
                    {
                        int i;
                        try
                        {
                            while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
                            {
                                MemoryStream memStream = new MemoryStream(bytes, 0, i);

                                BinaryFormatter bf = new BinaryFormatter();
                                NetworkMessage message = (NetworkMessage)bf.Deserialize(memStream);

                                MessageRecived(this, new MessageEventArgs(message));
                            }
                        }
                        catch (System.Runtime.Serialization.SerializationException ex)
                        {
                            Logger.Logger.Message(Logger.LogMessageType.Error, ex.Message);
                        }
                        catch (ArgumentNullException ex)
                        {
                            Logger.Logger.Message(Logger.LogMessageType.Error, ex.Message);
                        }
                        catch (Exception ex)
                        {
                            Logger.Logger.Message(Logger.LogMessageType.Error, ex.Message);
                            stream.Close();
                            if (client.Connected) client.Close();
                            Logger.Logger.Message(Logger.LogMessageType.Info, "Klient się rozłączył");
                            break;
                        }
                    }
                }
            ).Start();
        }

        void Client_MessageRecived(object sender, MessageEventArgs args)
        {
            //default implementation of event
        }
    }
}
